﻿using System;
using Microsoft.Xna.Framework.Graphics;

namespace Microsoft.Xna.Framework
{
	/// <summary>
	/// A game component that is notified when it needs to draw itself.
	/// </summary>
    public class DrawableGameComponent : GameComponent, IDrawable
	{
		#region Fields
		private bool _isVisible;
        private int _drawOrder;
		private bool initialized;
		private IGraphicsDeviceService deviceService;
		#endregion

		#region Properties
		/// <summary>
		/// Order in which the component should be drawn, relative to other components that are in the same GameComponentCollection.
		/// </summary>
		public int DrawOrder
		{
			get { return _drawOrder; }
			set
			{
				if (_drawOrder != value)
				{
					_drawOrder = value;

					OnDrawOrderChanged(this, EventArgs.Empty);
				}
			}
		}

		/// <summary>
		/// Indicates whether Draw should be called.
		/// </summary>
		public bool Visible
		{
			get { return _isVisible; }
			set
			{
				if (_isVisible != value)
				{
					_isVisible = value;
					OnVisibleChanged(this, EventArgs.Empty);
				}
			}

		}
		#endregion

		#region Events
		/// <summary>
		/// Raised when the DrawOrder property changes.
		/// </summary>
		public event EventHandler<EventArgs> DrawOrderChanged;

		/// <summary>
		/// Raised when the Visible property changes.
		/// </summary>
		public event EventHandler<EventArgs> VisibleChanged;
		#endregion

		#region Constructor
		/// <summary>
		/// Creates a new instance of DrawableGameComponent.
		/// </summary>
		/// <param name="game">The Game that the game component should be attached to.</param>
        public DrawableGameComponent(Game game)
            : base(game)
        {
			_isVisible = true;
        }
		#endregion

		#region Private Methods
		private void DeviceCreated(object sender, EventArgs e)
		{
			this.LoadContent();
		}

		private void DeviceDisposing(object sender, EventArgs e)
		{
			this.UnloadContent();
		}

		private void DeviceReset(object sender, EventArgs e)
		{
		}

		private void DeviceResetting(object sender, EventArgs e)
		{
		}
		#endregion

		#region Protected Methods
		/// <summary>
		/// Releases the unmanaged resources used by the DrawableGameComponent and optionally releases the managed resources.
		/// </summary>
		/// <param name="disposing">true to release both managed and unmanaged resources; false to release only unmanaged resources.</param>
		protected override void Dispose(bool disposing)
		{
			if (disposing)
			{
				this.UnloadContent();
				if (this.deviceService != null)
				{
					this.deviceService.DeviceCreated -= new EventHandler<EventArgs>(this.DeviceCreated);
					this.deviceService.DeviceResetting -= new EventHandler<EventArgs>(this.DeviceResetting);
					this.deviceService.DeviceReset -= new EventHandler<EventArgs>(this.DeviceReset);
					this.deviceService.DeviceDisposing -= new EventHandler<EventArgs>(this.DeviceDisposing);
				}
			}
			base.Dispose(disposing);
		}

		/// <summary>
		/// Called when graphics resources need to be loaded. Override this method to load any component-specific graphics resources.
		/// </summary>
		protected virtual void LoadContent()
		{
		}

		/// <summary>
		/// Called when the DrawOrder property changes. Raises the DrawOrderChanged event.
		/// </summary>
		/// <param name="sender">The DrawableGameComponent.</param>
		/// <param name="args">Arguments to the DrawOrderChanged event.</param>
		protected virtual void OnDrawOrderChanged(object sender, EventArgs args)
		{
			EventHandler<EventArgs> handler = this.DrawOrderChanged;
			if (handler != null)
			{
				handler(this, EventArgs.Empty);
			}
		}

		/// <summary>
		/// Called when the Visible property changes. Raises the VisibleChanged event.
		/// </summary>
		/// <param name="sender">The DrawableGameComponent.</param>
		/// <param name="args">Arguments to the VisibleChanged event.</param>
		protected virtual void OnVisibleChanged(object sender, EventArgs args)
		{
			EventHandler<EventArgs> handler = this.VisibleChanged;
			if (handler != null)
			{
				handler(this, EventArgs.Empty);
			}
		}

		/// <summary>
		/// Called when graphics resources need to be unloaded. Override this method to unload any component-specific graphics resources.
		/// </summary>
		protected virtual void UnloadContent()
		{
		}
		#endregion

		#region Public Methods
		/// <summary>
		/// Initializes the component. Override this method to load any non-graphics resources and query for any required services.
		/// </summary>
        public override void Initialize()
        {
            base.Initialize();
			if (!this.initialized)
			{
				this.deviceService = base.Game.Services.GetService(typeof(IGraphicsDeviceService)) as IGraphicsDeviceService;
				if (this.deviceService == null)
				{
					throw new InvalidOperationException(FrameworkResources.MissingGraphicsDeviceService);
				}
				this.deviceService.DeviceCreated += new EventHandler<EventArgs>(this.DeviceCreated);
				this.deviceService.DeviceResetting += new EventHandler<EventArgs>(this.DeviceResetting);
				this.deviceService.DeviceReset += new EventHandler<EventArgs>(this.DeviceReset);
				this.deviceService.DeviceDisposing += new EventHandler<EventArgs>(this.DeviceDisposing);
				if (this.deviceService.GraphicsDevice != null)
				{
					this.LoadContent();
				}
			}
			this.initialized = true;
        }

		/// <summary>
		/// Called when the DrawableGameComponent needs to be drawn. Override this method with component-specific drawing code.
		/// </summary>
		/// <param name="gameTime">Time passed since the last call to Draw.</param>
        public virtual void Draw(GameTime gameTime)
        {
		}
		#endregion
	}
}
